#include "Model/Calendar.hpp"
#include <boost/foreach.hpp>

namespace Model
{


Calendar::Calendar( const Time& start, const Time& end )
	: minTime_( start)
	, maxTime_( end)
{
}

// ---------------------------------------------------------------------------------------------------------------------

Calendar::~Calendar()
{
	BOOST_FOREACH( Rule* pRule, rules_)
	{
		delete pRule;
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::setMinimumTime( const Time& newTime )
{
	assert( newTime <= maxTime_);
	minTime_ = newTime;
	this->recalculateRules();
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::setMaximumTime( const Time& newTime )
{
	assert( newTime >= minTime_);
	maxTime_ = newTime;
	this->recalculateRules();
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::extendCalendar( const Time& minimumTime, const Time& maximumTime )
{
	assert( minimumTime <= maximumTime);
	minTime_ = minimumTime;
	maxTime_ = maximumTime;
	this->recalculateRules();
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::recalculateRules()
{
	PeriodSet newSet;
	
	BOOST_FOREACH( Rule* pCurRule, rules_)
	{
		Calendar::PeriodSet results;
		pCurRule->getPeriods( minTime_, maxTime_, results);
		newSet.merge( results);
	}

	busyPeriods_.swap( newSet);
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::addRule( Rule* pNewRule )
{
	assert( NULL != pNewRule);
	assert( rules_.end() == std::find( rules_.begin(), rules_.end(), pNewRule));
	rules_.push_back( pNewRule);
	this->recalculateRules();
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::removeRule( Rule* pExistingRule )
{
	assert( NULL != pExistingRule);
	assert( rules_.end() != std::find( rules_.begin(), rules_.end(), pExistingRule));
	rules_.remove( pExistingRule);
	this->recalculateRules();
}

// ---------------------------------------------------------------------------------------------------------------------

void Calendar::distributeDuration( const Time& start, const Duration& duration, PeriodSet& out )
{
	PeriodSet::const_iterator curI = busyPeriods_.begin(), endI = busyPeriods_.end();
	Duration remainingDuration = duration;

	// Find the first start of the distribution
	Time dStart = start;
	while( curI != endI && curI->begin() < start)
	{
		if( curI->contains( start))
		{
			dStart = curI->end();
		}
		++curI;
	}

	while( curI != endI)
	{
		Duration diff = curI->begin() - dStart;
		if( diff > remainingDuration)
		{
			Time dEnd = dStart + remainingDuration;
			remainingDuration = Duration(0,0,0);
			out.merge( dStart, dEnd);
			return; // Exit point here!
		}
		else 
		{
			remainingDuration -= diff;
			Time dEnd = curI->begin();
			out.merge( dStart, dEnd);
			dStart = curI->end();
		}
		++curI;
	}

	if( remainingDuration > Duration(0,0,0))
	{
		Time dEnd = dStart + remainingDuration;
		out.merge( dStart, dEnd);
	}
}

} // namespace Model
